#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "proto.h"

enum line_stat
{
	LINE_RETURN_TYPE = 0,
	LINE_FUNCTION_NAME,
	LINE_PARAM,
	LINE_CONTENT,
};

void delspace(char *str, char *out)
{
	int	ret;
	char	*p1,*p2;

	p1 = str;
	p2 = out;
	while(1)
	{
		if(*p1 == 0)
		{
			*p2 = 0;
			break;
		}
		ret = isspace(*p1);
		if(ret)
		{
			p1++;
		}
		else
		{
			*p2 = *p1;
			p1++;
			p2++;
		}
	}
}

void	delstar(char *str)
{
	char	tmp[128] = {0}, *src, *dst;

	src = str;
	dst = tmp;
	while(*src != 0) 
	{
		if(*src != '*')
		{
			*dst = *src;
			dst++;
		}
		src++;
	}
	strcpy(str, tmp);
}

char	get_first(char *str)
{
	int	ret;
	char	*p = str;

	while(1)
	{
		if(*p == 0) break;
		ret = isspace(*p);
		if(ret) {	
			p++;
			continue;
		}
		return *p;
	}
	return '#';
}

int	is_ignore(char *str)
{
	char	c;

	c = get_first(str);
	if(c == '#') return 1;
	return 0;
}

void	func_set_return_type(func_t *func, char *str)
{
	char	out[128] = {0};

	delspace(str, out);
	strcpy(func->return_type.name, out);
}

void	func_set_name(func_t *func, char *str)
{
	char	out[128] = {0};

	delspace(str, out);
	strcpy(func->name, out);
}

void	line_get_first_word(char *str, char **start, char **end)
{
	int 	in_str = 0;
	char	*p = str;

	while(1)
	{
		if(isspace(*p) || *p == ',') 
		{
			if(in_str == 1)
			{
				(*end) = p - 1;
				return;
			}
			p++;
			continue;
		}
		if(in_str == 0)
		{
			(*start) = p;
			in_str = 1;
		}
		p++;
	}
}

void	line_split(char *str, uint8_t *desc, char *type, char *name)
{
	char	*start, *end, desc_str[8] = {0}, out[128];

	(*desc) = 0;
	line_get_first_word(str, &start, &end);
	memcpy(desc_str, start, end - start + 1); 
	if(strstr(desc_str, "I")) (*desc) |= PARAM_IN;
	if(strstr(desc_str, "O")) (*desc) |= PARAM_OUT;
	if(strstr(desc_str, "X")) (*desc) |= (PARAM_IN | PARAM_OUT);
	if(strstr(desc_str, "V")) (*desc) |= PARAM_VALUE; 
	if(strstr(desc_str, "P")) (*desc) |= PARAM_POINT; 
	if(strstr(desc_str, "S")) (*desc) |= PARAM_SINGLE; 
	if(strstr(desc_str, "M")) (*desc) |= PARAM_MULTIP; 
	if(strstr(desc_str, "D")) (*desc) |= PARAM_DATA; 

	line_get_first_word(end + 1, &start, &end);
	memset(out, 0, 128);
	memcpy(out, start, end - start + 1); 
	delspace(out, type);

	line_get_first_word(end + 1, &start, &end);
	memset(out, 0, 128);
	memcpy(out, start, end - start + 1); 
#if 0
	if((*desc) & PARAM_POINT) {
		/* del star */
		delstar(out);
	}
#endif
	delspace(out, name);
}

void	func_set_param(func_t *func, char *str)
{
	PARAM *cur;
	uint8_t	desc;
	char type[128] = {0}, name[128] = {0}, *p;

	func->pa = (PARAM *)realloc(func->pa, (func->pa_num + 1) * sizeof(PARAM));
	cur = func->pa + func->pa_num;
	memset(cur, 0, sizeof(PARAM));
	line_split(str, &desc, type, name);
	cur->desc = desc;
	strcpy(cur->type.name, type);
	p = strstr(type, "_PTR");	
	if(p != NULL)
	{
		cur->type.is_point = 1;
		memcpy(cur->type.type_name, type, p - type);
	}
	strcpy(cur->name, name);

	if((desc & PARAM_OUT) && ((desc & PARAM_DATA) || (desc & PARAM_MULTIP)))
	{
		func->dataout_num++;
		func->pa_dataout = malloc(sizeof(PARAM));
		memset(func->pa_dataout, 0, sizeof(PARAM));
		memcpy(func->pa_dataout, cur, sizeof(PARAM));
	}

	if((desc & PARAM_MULTIP)) {
		func->has_loop++;
	}
	func->pa_num++;
}

int	proto_load(const char *fname, func_t **funcs, int *num)
{
	int	cur_stat = LINE_RETURN_TYPE;
	int	content_flag_num = 0;
	FILE	*fp;
	char	buf[1024], *p;
	func_t	*out = NULL, *cur;
	int	count = 0;

	fp = fopen(fname, "r");
	if(fp == NULL) {
		printf("proto_load open %s failed.\n", fname);
		return -1;
	}
	while(!feof(fp))
	{
		memset(buf, 0, 1024);
		p = fgets(buf, 1024, fp);
		if(p == NULL) {
			break;
		}
		if(is_ignore(buf)) {
			continue;
		}

		if(cur_stat == LINE_RETURN_TYPE)
		{
			out = (func_t *)realloc(out, (count + 1) * sizeof(func_t)); 
			cur = out + count;
			memset(cur, 0, sizeof(func_t));
			count++;
			func_set_return_type(cur, buf);
			cur_stat = LINE_FUNCTION_NAME;
		}
		else if(cur_stat == LINE_FUNCTION_NAME)
		{
			func_set_name(cur, buf);
			cur_stat = LINE_PARAM;
		}
		else if(cur_stat == LINE_PARAM)
		{
			if(strstr(buf, "(")) {
				continue;
			}
			if(strstr(buf, ")")) 
			{
				if(strstr(buf, ";"))
				{
					cur_stat = LINE_RETURN_TYPE;
					continue;
				}
				else
				{
					cur_stat = LINE_CONTENT;
					continue;
				}
			}
			func_set_param(cur, buf);
		}
		else if(cur_stat == LINE_CONTENT)
		{
			if(strstr(buf, "{")) {
				if(cur->code == NULL) {
					cur->code = malloc(16384);
					memset(cur->code, 0, 16384);
				}
				p = strstr(buf, "}");
				if(p) {
					strcat(cur->code, buf);
					continue;
				}
				content_flag_num++;
				if(content_flag_num > 1)
				{
					strcat(cur->code, buf);
				}
				continue;
			}
			if(strstr(buf, "}")) {
				content_flag_num--;
				if(content_flag_num == 0)
				{
					cur_stat = LINE_RETURN_TYPE;
					continue;
				}
				strcat(cur->code, buf);
				continue;
			}
			strcat(cur->code, buf);
		}
	}
	fclose(fp);
	(*num) = count;
	(*funcs) = out;
	return 0;
}
